use std::ops::Add;
use std::rc::Rc;
use chrono::{Local, TimeDelta};
use gloo::timers::callback::Timeout;
use crate::{Callback, TENANT_CODE};
use yew::{function_component, html, use_context, use_effect_with, use_reducer, Html, Properties, Reducible, UseReducerHandle};
use yew::platform::spawn_local;
use yew_hooks::{use_local_storage, UseLocalStorageHandle};
use yew_router::navigator::Navigator;
use yew_router::prelude::use_navigator;
use yewdux::use_dispatch;
use crate::business::im_router::{ImRoute};
use crate::business::app_state::{AppState};
use crate::business::page::fragment::im_session_list_view::{SessionListState};
use crate::business::service::check_hot_message_service::CheckHotMessageService;
use crate::business::service::syn_data_service::SynDataService;
use crate::business::util::css::{add_class, remove_class, set_class_style};
use crate::business::util::input::{get_input_value};
use crate::business::util::local_seq::init_local_seq;
use crate::business::util::util::get_login_info;
use crate::business::websocket::receive_send_handle::{ImReceiveSendHandleMsg};
use crate::business::websocket::send_data_router::{RpcRequest, RpcResponse};
use crate::business::websocket::send_data_router::RpcResponse::LoginResponse;
use crate::business::websocket::dto::common_dto::{LoginCookie};
use crate::business::websocket::dto::user_dto::{LoginReq, LoginTokenReq, Reconnection, TerminalType};
use crate::business::websocket::send_data_handle::{send_data_async, send_data_callback};
use crate::business::websocket::websocket_view::ImWebSocketStatus;

#[function_component(LoginPage)]
pub fn syn_data_page() -> Html {
    let app_state = use_context::<AppState>().unwrap();
    let app_state_clone = app_state.clone();

    let navigator = use_navigator().unwrap();
    let load_data_reducer = use_reducer(|| LoadDataReducer {
        status: LoadStatus::Init,
        progress: 0.0,
        message: None,
        navigator: navigator.clone(),
    });

    let session_dispatch = use_dispatch::<SessionListState>();

    let load_data_reducer_clone = load_data_reducer.clone();
    let syn_data_callback = Callback::from(move |user_id: String| {
        let app_state_clone2 = app_state_clone.clone();
        let load_data_reducer_clone2 = load_data_reducer_clone.clone();

        let user_id_clone = user_id.clone();
        let session_dispatch_clone = session_dispatch.clone();
        Timeout::new(100, move || {
            let syn_data_service = SynDataService;

            let start_date = Local::now();
            let start_date = start_date.add(TimeDelta::days(-14));
            CheckHotMessageService::check_hot_message_total(session_dispatch_clone.clone(), user_id_clone.clone(), start_date);

            spawn_local(async move {
                syn_data_service.syn_data(app_state_clone2, user_id.clone(), load_data_reducer_clone2).await;
            });
        }).forget();
    });

    html! {
        <LoginView login_success_callback = {syn_data_callback} load_data_reducer={load_data_reducer.clone()} />
    }
}

#[derive(Properties, Clone, PartialEq)]
struct LoginViewProps {
    login_success_callback: Callback<String>,
    load_data_reducer: UseReducerHandle<LoadDataReducer>,
}

#[function_component(LoginView)]
fn login_view(props: &LoginViewProps) -> Html {
    let mut is_logined = false;
    let mut login_token = None;

    let local_storage = use_local_storage::<String>("_login_".to_string());
    match get_login_info(local_storage) {
        None => {}
        Some(token) => {
            is_logined = !token.is_expired();
            login_token = Some(token);
        }
    }

    log::info!("login is login: {}", is_logined);
    let login_success_callback = props.login_success_callback.clone();
    let load_data_reducer = props.load_data_reducer.clone();
    let html = if is_logined {
        let login_token = login_token.unwrap();
        html!{ <LoginTokenForm token={ login_token.token.clone() } user_id = {login_token.use_id.clone()}
            machine_code = {login_token.machine_code.clone()} {login_success_callback} {load_data_reducer}></LoginTokenForm> }
    } else {
        html! { <LoginForm {login_success_callback} {load_data_reducer}/> }
    };

    html!{
        {html}
    }
}

fn do_login_business(callback: Callback<String>, login_response: RpcResponse, local_storage: UseLocalStorageHandle<String>, machine_code: String,
                 set_login_user_id_callback: Callback<String>) {

    if let LoginResponse(result) = login_response {
        match result {
            Ok(login_resp) => {
                log::info!("loginSuccess: {}", &login_resp.local_seq);

                let user_id = login_resp.user_id.clone();
                set_login_user_id_callback.emit(user_id.clone());
                
                let server_date_time = login_resp.server_date_time.clone();
                init_local_seq(server_date_time);

                let now = Local::now().timestamp_millis() as u64;
                let expired = now + 20 * 60 * 60 * 1000;
                let login_token = LoginCookie {
                    token: login_resp.token.clone(),
                    auth_token: login_resp.auth_token.clone(),
                    use_id: login_resp.user_id.clone(),
                    machine_code,
                    expired,
                };
                let json = serde_json::to_string(&login_token).unwrap();
                local_storage.set(json);

                callback.emit(user_id);
            }
            Err(exception_resp) => {
                log::error!("login error: {:?}", exception_resp);
            }
        }
    }
}

#[derive(Properties, Clone, PartialEq)]
struct LoginFormProps {
    login_success_callback: Callback<String>,
    load_data_reducer: UseReducerHandle<LoadDataReducer>,
}

#[function_component(LoginForm)]
fn login_form(props: &LoginFormProps) -> Html {
    let app_state = use_context::<AppState>().unwrap();
    let load_data_reducer = props.load_data_reducer.clone();

    let state2 = app_state.clone();

    let local_storage = use_local_storage::<String>("_login_".to_string());

    let navigator = use_navigator().unwrap();
    let navigator2 = navigator.clone();

    let login_success_callback = props.login_success_callback.clone();
    let login_action = Callback::from(move |_| {
        remove_class("loginButton", "login-button");
        add_class("loginButton", "login-button-disable");
        set_class_style("loader", "visibility: visible");

        let machine_code: String = create_machine_code();
        let machine_code_clone_2 = machine_code.clone();

        let user_id = get_input_value("user-name");
        let password = get_input_value("password");
        let login_req = LoginReq {
            user_id: user_id.unwrap(),
            password: password.unwrap(),
            machine_code,
            tenant_code: TENANT_CODE.to_string(),
            terminal_type: TerminalType::Web,
            create_date_time: Local::now(),
            local_seq: format!("{}_00001", Local::now().timestamp_nanos_opt().unwrap_or(1111111)),
        };

        log::info!("begin login 1111111111111, {}", state2.receive_send_handle.is_some());

        let set_login_user_id_callback = state2.set_login_user_id_callback.clone().unwrap();
        // let navigator = navigator2.clone();
        let new_local_storage = local_storage.clone();

        // login_callback(login_req, state2, new_local_storage, login_success_callback);

        let login_success_callback_clone = login_success_callback.clone();

        let state_clone = state2.clone();
        spawn_local(async move {
            let receive_data_handle = state_clone.receive_send_handle.clone().unwrap();
            let response = send_data_async(state_clone, RpcRequest::Login(login_req)).await;
            let t = new_local_storage.clone();
            do_login_business(login_success_callback_clone.clone(), response, t, machine_code_clone_2.clone(), set_login_user_id_callback.clone());

            // navigator.push(&ImRoute::MainRoot);
        });

    });

    html! {
        <div class="center-container">
            <div class="bank-container">
                <div class="login-container">
                    <h2>{ "宇联通-登录" }</h2>
                    <div class="form-group">
                        <label for="username"> { "用户名:" } </label>
                        <input type="text" id="user-name" name="username" value="test00100"/>
                    </div>
                    <div class="form-group">
                        <label for="password"> { "密码: " }</label>
                        <input type="password" id="password" name="password" value="123456"/>
                    </div>

                    <div class="loader-container2" id="loader">
                        <div class="progress-container2">
                            <div class="progress-bar" id="progress-bar"></div>
                        </div>

                        <div class="loading-text2">
                            <span style="--i:1">{ load_data_reducer.message.clone()}</span>
                        </div>
                    </div>

                    <button onclick= { login_action } class="login-button" id="loginButton"> { "登录" } </button>
                </div>
            </div>
        </div>
    }
}

#[derive(Properties, Clone, PartialEq)]
struct ReconnectionFormProps {
    auth_token: String,
    login_success_callback: Callback<String>,
}

#[function_component(ReconnectionForm)]
fn reconnect(props: &ReconnectionFormProps) -> Html {
    let app_state = use_context::<AppState>().unwrap();
    let state2 = app_state.clone();
    let local_storage = use_local_storage::<String>("_login_".to_string());

    let navigator = use_navigator().unwrap();
    let navigator2 = navigator.clone();

    let auth_token = props.auth_token.clone();

    let websocket_status = state2.websocket_status.clone();
    let login_success_callback = props.login_success_callback.clone();

    use_effect_with(websocket_status, move |websocket_status| {
        // let state_clone = state2.clone();

        if *websocket_status == ImWebSocketStatus::Connected {
            let reconnection_req = Reconnection {
                auth_token,
                tenant_code: TENANT_CODE.to_string(),
                terminal_type: TerminalType::Web,
                create_date_time: Local::now(),
                local_seq: format!("{}_00002", Local::now().timestamp_nanos_opt().unwrap_or(1111111)),
            };

            log::info!("begin login 1111111111111",);

            let navigator = navigator2.clone();
            let new_local_storage = local_storage.clone();
            let set_login_user_id_callback = state2.set_login_user_id_callback.clone().unwrap();

            let callback = Callback::from(move |response: RpcResponse| {
                let t = new_local_storage.clone();
                do_login_business(login_success_callback.clone(), response, t, "web".to_string(), set_login_user_id_callback.clone());
                // navigator.push(&ImRoute::MainRoot);
                log::info!("loginSuccess: redirect index");
            });

            let websocket_handle = state2.receive_send_handle.clone();
            if let Some(websocket_handle) = websocket_handle {
                websocket_handle.dispatch(ImReceiveSendHandleMsg::Request(RpcRequest::Reconnection(reconnection_req), callback));
            } else {
                log::info!("no websocket handle");
            }
        }

        ||()
    });

    html! {
        <div class="center-container">
            <div class="bank-container">
                <div class="loader-container" id="loader">
                    <div class="loader">
                        <div class="loader-circle"></div>
                        <div class="loader-circle"></div>
                        <div class="loader-circle"></div>
                    </div>

                    <div class="progress-container">
                        <div class="progress-bar" id="progress-bar"></div>
                    </div>

                    <div class="loading-text">
                        <span style="--i:1">{"加"}</span>
                        <span style="--i:2">{"载"}</span>
                        <span style="--i:3">{"中"}</span>
                        <span style="--i:4">{"."}</span>
                        <span style="--i:5">{"."}</span>
                        <span style="--i:6">{"."}</span>
                    </div>
                </div>
            </div>
        </div>
    }
}

#[derive(Properties, Clone, PartialEq)]
struct LoginTokenFormProps {
    token: String,
    user_id: String,
    machine_code: String,
    login_success_callback: Callback<String>,
    load_data_reducer: UseReducerHandle<LoadDataReducer>,
}

#[function_component(LoginTokenForm)]
fn login_by_token(props: &LoginTokenFormProps) -> Html {
    let load_data_reducer = props.load_data_reducer.clone();

    let app_state = use_context::<AppState>().unwrap();
    let state2 = app_state.clone();
    let local_storage = use_local_storage::<String>("_login_".to_string());

    let navigator = use_navigator().unwrap();
    let navigator2 = navigator.clone();

    let user_id = props.user_id.clone();
    let token = props.token.clone();
    let machine_code = props.machine_code.clone();

    let websocket_status = app_state.websocket_status.clone();
    let login_success_callback = props.login_success_callback.clone();
    use_effect_with(websocket_status, |ws_status| {
        if let ImWebSocketStatus::Connected = ws_status {
            let login_token_req = LoginTokenReq {
                user_id,
                token,
                machine_code,
                tenant_code: TENANT_CODE.to_string(),
                terminal_type: TerminalType::Web,

                create_date_time: Local::now(),
                local_seq: format!("{}_00003", Local::now().timestamp_nanos_opt().unwrap_or(1111111)),
            };

            // token_login(user_id, token, machine_code, state2, navigator2, local_storage, login_success_callback);

            async_token_login(login_token_req, state2, navigator2, local_storage, login_success_callback);
        }

        || ()
    });

    html! {
        <div class="bank-container">
            <div class="loader-container" id="loader">
                <div class="loader">
                    <div class="loader-circle"></div>
                    <div class="loader-circle"></div>
                    <div class="loader-circle"></div>
                </div>

                <div class="progress-container">
                    <div class="progress-bar" id="progress-bar"></div>
                </div>

                <div class="loading-text">
                    <span style="--i:1">{ load_data_reducer.message.clone() }</span>
                </div>
            </div>
        </div>
    }
}

fn login_callback(login_req : LoginReq, state2: AppState, local_storage: UseLocalStorageHandle<String>, login_success_callback: Callback<String>) {
    let login_req_copy = login_req.clone();
    let login_success_callback_copy = login_success_callback.clone();

    let set_login_user_id_callback = state2.set_login_user_id_callback.clone().unwrap();
    let callback = Callback::from(move |response: RpcResponse| {
        let t = local_storage.clone();
        do_login_business(login_success_callback_copy.clone(), response, t, login_req_copy.machine_code.clone(), set_login_user_id_callback.clone());
        // navigator.push(&ImRoute::MainRoot);
        log::info!("loginSuccess: redirect index");
    });

    send_data_callback(state2, RpcRequest::Login(login_req), callback);
}

fn async_login(_login_req : LoginReq, _state2: AppState, _local_storage: UseLocalStorageHandle<String>) {

}

fn async_token_login(login_token_req: LoginTokenReq, state2: AppState, _navigator: Navigator,
               local_storage: UseLocalStorageHandle<String>, login_success_callback: Callback<String>) {

    let machine_code_clone: String = login_token_req.machine_code.clone();

    log::info!("begin login 2222, {}", state2.receive_send_handle.is_some());

    let new_local_storage = local_storage.clone();
    let set_login_user_id_callback = state2.set_login_user_id_callback.clone().unwrap();

    spawn_local(async move {
        log::info!("login await!!!!");
        let response = send_data_async(state2, RpcRequest::LoginByToken(login_token_req)).await;

        let t = new_local_storage.clone();
        log::info!("login end!!!!");
        do_login_business(login_success_callback.clone(), response, t, machine_code_clone, set_login_user_id_callback);
        // navigator.push(&ImRoute::MainRoot);
        // log::info!("login success: redirect index");
    });
}

fn token_login(login_token_req: LoginTokenReq, state2: AppState, _navigator: Navigator,
         local_storage: UseLocalStorageHandle<String>, login_success_callback: Callback<String>) {
    let websocket_status = state2.websocket_status.clone();
    let set_login_user_id_callback = state2.set_login_user_id_callback.clone().unwrap();


    if websocket_status == ImWebSocketStatus::Connected {
        log::info!("begin login 2222, {}", state2.receive_send_handle.is_some());

        let new_local_storage = local_storage.clone();
        let login_success_callback_clone = login_success_callback.clone();
        let machine_code_clone = login_token_req.machine_code.clone();

        let callback = Callback::from(move |response: RpcResponse| {
            let t = new_local_storage.clone();
            do_login_business(login_success_callback_clone.clone(), response, t, machine_code_clone.clone(), set_login_user_id_callback.clone());
            // navigator.push(&ImRoute::MainRoot);
            log::info!("loginSuccess: redirect index");
        });

        send_data_callback(state2, RpcRequest::LoginByToken(login_token_req), callback);
    }
}

fn create_machine_code()-> String {
    format!("web-{}", Local::now().timestamp())
}

#[derive(PartialEq)]
pub struct LoadDataReducer {
    status: LoadStatus,
    progress: f32,
    message: Option<String>,
    navigator: Navigator,
}

pub enum LoadDataAction {
    SendMsg(String),
    SendProgressAndMsg(f32, String),
    Done(String),
}

impl Reducible for LoadDataReducer {
    type Action = LoadDataAction;

    fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
        match action {
            LoadDataAction::SendMsg(msg) => {
                Self {
                    status: LoadStatus::Loading,
                    progress: self.progress,
                    message: Some(msg),
                    navigator: self.navigator.clone()
                }.into()
            }
            LoadDataAction::SendProgressAndMsg(progress, msg) => {
                let progress_total = if self.progress + progress > 100.0 {
                    100.0
                } else {
                    self.progress + progress
                };
                set_class_style("progress-bar", format!("width: {}%", progress_total).as_str());

                Self {
                    status: LoadStatus::Loading,
                    progress: progress_total,
                    message: Some(msg),
                    navigator: self.navigator.clone()
                }.into()
            }
            LoadDataAction::Done(msg) => {
                log::info!("syn data done: {}", &msg);
                set_class_style("progress-bar", "width: 100%");

                let navigator = self.navigator.clone();
                let navigator_clone = navigator.clone();
                Timeout::new(500, move || {
                    navigator_clone.push(&ImRoute::MainRoot);
                }).forget();

                Self {
                    status: LoadStatus::Loaded,
                    progress: 100.0,
                    message: Some(msg),
                    navigator
                }.into()
            }
        }
    }
}

#[derive(PartialEq)]
pub enum LoadStatus {
    Init,
    Loading,
    Loaded,
    Error,
}